package com.miku.springaialibabalearn.controller;


import com.miku.springaialibabalearn.entity.SingerSongs;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
// @RestController 注解：
// 这是一个组合注解，等同于 @Controller 和 @ResponseBody。
// @Controller 标记这是一个 Spring MVC 控制器，用于处理 Web 请求。
// @ResponseBody 标记类中所有方法的返回值都应直接作为 HTTP 响应体发送，而不是解析为视图名称。
@RestController
// @CrossOrigin 注解：
// 启用跨域资源共享 (CORS)。
// origins = "http://localhost:5173" 指定只允许来自 "http://localhost:5173" 这个源（通常是前端应用的地址）的请求访问此控制器。
// 这对于前后端分离开发非常有用，避免浏览器因同源策略而阻止请求。
@CrossOrigin(origins = "http://localhost:5173")
public class ChatClientController { // 定义一个名为 ChatClientController 的公共类

    // 声明一个私有的 final 字段 chatClient，类型为 ChatClient。
    // final 关键字表示这个字段只能被赋值一次，通常在构造函数中。
    // 它将持有用于与 AI 模型进行高级交互的 ChatClient 实例。
    private final ChatClient chatClient;

    // 声明一个私有的 final 字段 dashscopeChatModel，类型为 ChatModel。
    // 这个字段用于持有通过依赖注入获取的 DashScope ChatModel 实例。
    // 虽然 ChatClient 内部会使用这个 Model，但在这里也保留一个引用。
    private final ChatModel dashscopeChatModel;

    // 构造函数：
    // Spring 会自动检测到这个构造函数，并通过依赖注入 (DI) 机制查找所需的 Bean。
    // 这个构造函数用于注入一个 ChatModel Bean。
    public ChatClientController(
            // @Qualifier("dashscopeChatModel") 注解：
            // 当 Spring 容器中有多个类型为 ChatModel 的 Bean 时，使用 @Qualifier 注解指定要注入的 Bean 的名称。
            // 这里指定注入名称为 "dashscopeChatModel" 的 ChatModel Bean。
            // 这解决了之前遇到的 ChatModel 依赖歧义问题。
            @Qualifier("dashscopeChatModel") ChatModel chatModel
    ) {
        // 将注入的 ChatModel 实例赋值给类的 dashscopeChatModel 字段。
        this.dashscopeChatModel = chatModel;

        // 创建一个 ChatClient.Builder 实例。
        // ChatClient.builder() 是一个静态工厂方法，用于获取 ChatClient 的构建器。
        // 将注入的 dashscopeChatModel 传递给 builder() 方法，表示这个 ChatClient 将使用这个特定的 ChatModel。
        ChatClient.Builder builder = ChatClient.builder(dashscopeChatModel);

        // 使用 builder 的 build() 方法构建最终的 ChatClient 实例。
        // build() 方法根据 builder 的配置创建一个 ChatClient 对象。
        // 将构建好的 ChatClient 实例赋值给类的 chatClient 字段。
        this.chatClient = builder.build();

        // or create a ChatClient with the default builder settings:
        // 这是一行被注释掉的代码，表示另一种创建 ChatClient 的方式：
        // ChatClient.builder().build(); // 使用默认配置创建 ChatClient。
        // 这种方式会尝试查找一个默认的 ChatModel Bean（如果没有 @Primary 或只有一个 ChatModel Bean）。
        // 在当前场景下，由于我们明确指定了要使用 dashscopeChatModel，所以上面通过 builder(dashscopeChatModel) 的方式是正确的。
    }

    @GetMapping("/chat")
    // 将 HTTP GET 请求映射到 /chat 路径
    public String chat( @RequestParam("input") String input ) {
        // @RequestParam("input") String input 表示从请求参数中获取名为 "input" 的值，并赋给 input 变量

        // 使用 chatClient 的 Fluent API 开始构建一个请求
        return this.chatClient.prompt()
                // 1. 调用 prompt() 方法开始构建一个 Prompt (提示)
                .user(input)
                // 2. 调用 user() 方法设置用户角色的消息内容，内容为传入的 input 字符串
                .call()
                // 3. 调用 call() 方法，执行与 AI 模型的同步调用 (发送请求并等待响应)
                .content();
                // 4. 调用 content() 方法，从 AI 模型的响应 (ChatResponse) 中提取文本内容并返回
    }





    @GetMapping( "/response" )
    public ChatResponse response(String input ) {

        return this.chatClient.prompt()
                .user(input)
                // 设置了用户消息的内容
                .call()
                // call 方法向 AI 模型发送请求
                .chatResponse();
                // 调用 chatResponse() 返回 ChatResponse

    }



    // 定义一个 HTTP GET 请求处理方法，映射到 "/singer-songs" 路径
    @GetMapping("/singer-songs")
    // 返回类型为 SingerSongs，表示该方法会返回一个 SingerSongs 类型的对象，Spring MVC 会自动将其转换为 JSON 格式返回给客户端
    public SingerSongs chatEntity(@RequestParam("name") String name) {
        // 使用 @RequestParam("name") 注解将 HTTP 请求参数 "name" 绑定到方法参数 String name 上

        // 使用 chatClient 的 Fluent API 构建 Prompt 并调用 AI 模型
        return this.chatClient.prompt()
                // 1. 开始构建 Prompt (提示) 对象，prompt() 方法是 Fluent API 的起始点
                .user("Generate a list of songs of the singer " + name + ". Respond in JSON format with keys 'singer' and 'songs'. ")
                // 2. 设置用户消息 (User Message)，作为 Prompt 的内容
                //    - user() 方法指定消息的角色为用户 (User)
                //    - 消息内容是动态生成的字符串，包含用户输入的歌手名字 (name)
                //    - 指示 AI 模型生成指定歌手的歌曲列表，并明确要求以 JSON 格式返回
                //    - JSON 格式要求包含 "singer" 和 "songs" 两个键
                .call()
                // 3. 调用 call() 方法，执行与 AI 模型的同步请求并等待响应
                //    - call() 方法会将之前构建的 Prompt 发送给配置好的 AI 模型服务
                //    - 它会返回一个 ChatResponse 对象，包含了 AI 模型的响应结果
                .entity(SingerSongs.class);
                // 4. 调用 entity(SingerSongs.class) 方法，将 AI 模型的响应结果映射为 SingerSongs 类型的对象
                //    - entity() 方法假设 AI 模型返回的响应是 JSON 格式的字符串
                //    - 它会自动将 JSON 字符串反序列化 (解析) 成 SingerSongs 类的实例
                //    - SingerSongs.class 指定了目标实体类的类型，Spring AI 会根据这个类型进行映射

        // 整个方法流程：
        // 1. 接收 HTTP GET 请求，并从请求参数中获取歌手名字 (name)。
        // 2. 构建一个 Prompt，包含指示 AI 模型生成歌手歌曲列表的指令，并要求 JSON 格式输出。
        // 3. 使用 ChatClient 调用 AI 模型，发送 Prompt 并获取 ChatResponse。
        // 4. 将 ChatResponse 中的 JSON 格式内容映射为 SingerSongs 对象。
        // 5. 返回 SingerSongs 对象，Spring MVC 会自动将其转换为 JSON 格式返回给客户端。
    }



    @GetMapping( value = "/stream-chat",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    /*使用 @GetMapping 注解，将 HTTP GET 请求路径 "/stream-chat" 映射到 streamChat 方法
    value = "/stream-chat" 指定了请求的 URL 路径
    produces = MediaType.TEXT_EVENT_STREAM_VALUE 属性作用:
    produces 属性指定了该方法生成的响应的 MIME 类型 (Media Type)。它告诉客户端，服务器将返回的内容的类型。MediaType.TEXT_EVENT_STREAM_VALUE 特别指定了响应类型为 text/event-stream。
    MIME 类型: MIME 类型是一种标准，用于标识文档、文件或字节流的性质和格式。例如，text/html 表示 HTML 文档，application/json 表示 JSON 数据。
    MediaType.TEXT_EVENT_STREAM_VALUE: 这个常量是 Spring Framework 中 MediaType 类定义的一个静态常量，它代表 MIME 类型 text/event-stream。
    Server-Sent Events (SSE): text/event-stream MIME 类型指示服务器将使用 Server-Sent Events (SSE) 技术来发送响应。*/
    public Flux<String> streamChat(@RequestParam("input") String input) {
        // 定义名为 streamChat 的方法，处理 "/stream-chat" 的 GET 请求
        // 方法的返回类型是 Flux<String>，表示一个包含多个 String 元素的异步数据流 (Reactive Stream)
        // 使用 @RequestParam("input") 注解，将 HTTP 请求参数名为 "input" 的值绑定到方法的 input 参数上，类型为 String

        return this.chatClient.prompt()
                // 1. 使用 chatClient 的 Fluent API 开始构建 Prompt (提示) 对象
                // prompt() 方法开始构建一个新的 Prompt，它是与 AI 模型交互的输入
                .user(input)
                // 2. 设置用户消息 (User Message) 到 Prompt 中
                // user(input) 方法指定消息的角色为 "user"，消息内容为方法的参数 input (用户输入)
                .stream()
                // 3. 调用 stream() 方法，执行与 AI 模型的流式交互
                // stream() 方法会将构建好的 Prompt 发送给配置的 AI 模型服务，并启动流式响应
                // 返回值是一个 Flux<ChatResponse>，表示 ChatResponse 对象的流
                .content();
                // 4. 调用 content() 方法，从 Flux<ChatResponse> 中提取内容并转换为 Flux<String>
                // content() 方法将 Flux<ChatResponse> 转换为 Flux<String>，只保留每个 ChatResponse 中的文本内容 (getContent())
                // 最终返回 Flux<String>，每个 String 元素代表模型流式输出的一部分文本

        // 整个 streamChat 方法的流程：
        // 1.接收用户通过 HTTP GET 请求传递的 "input" 参数作为用户输入
        // 2.构建一个 Prompt，包含用户输入的消息
        // 3.使用 ChatClient 发起流式聊天请求，将 Prompt 发送给 AI 模型
        // 4.从模型返回的流式 ChatResponse 中提取文本内容，并作为 Flux<String> 返回
        // 5.Spring WebFlux 会将返回的 Flux<String> 作为 Server-Sent Events (SSE) 流式响应返回给客户端
    }

    @GetMapping( value = "/stream-chat-response",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    /*使用 @GetMapping 注解，将 HTTP GET 请求路径 "/stream-chat-response" 映射到 streamChatResponse 方法
    value = "/stream-chat-response" 指定了请求的 URL 路径
    produces = MediaType.TEXT_EVENT_STREAM_VALUE 属性作用:
    produces 属性指定了该方法生成的响应的 MIME 类型 (Media Type)。它告诉客户端，服务器将返回的内容的类型。MediaType.TEXT_EVENT_STREAM_VALUE 特别指定了响应类型为 text/event-stream。
    MIME 类型: MIME 类型是一种标准，用于标识文档、文件或字节流的性质和格式。例如，text/html 表示 HTML 文档，application/json 表示 JSON 数据。
    MediaType.TEXT_EVENT_STREAM_VALUE: 这个常量是 Spring Framework 中 MediaType 类定义的一个静态常量，它代表 MIME 类型 text/event-stream。
    Server-Sent Events (SSE): text/event-stream MIME 类型指示服务器将使用 Server-Sent Events (SSE) 技术来发送响应。*/
    public Flux<ChatResponse> streamChatResponse(@RequestParam("input") String input) {
        // 定义名为 streamChatResponse 的方法，处理 "/stream-chat-response" 的 GET 请求
        // 方法的返回类型是 Flux<ChatResponse>，表示一个包含多个 ChatResponse 对象的异步数据流
        // 使用 @RequestParam("input") 注解，将 HTTP 请求参数名为 "input" 的值绑定到方法的 input 参数上，类型为 String

        return this.chatClient.prompt()
                // 1. 使用 chatClient 的 Fluent API 开始构建 Prompt (提示) 对象
                // prompt() 方法开始构建一个新的 Prompt，它是与 AI 模型交互的输入
                .user(input)
                // 2. 设置用户消息 (User Message) 到 Prompt 中
                // user(input) 方法指定消息的角色为 "user"，消息内容为方法的参数 input (用户输入)
                .stream()
                // 3. 调用 stream() 方法，执行与 AI 模型的流式交互
                // stream() 方法会将构建好的 Prompt 发送给配置的 AI 模型服务，并启动流式响应
                // 返回值是一个 Flux<ChatResponse>，表示 ChatResponse 对象的流
                .chatResponse();
                // 4. 调用 chatResponse() 方法，直接返回 Flux<ChatResponse> 对象流
                // chatResponse() 方法直接返回 stream() 方法返回的 Flux<ChatResponse>，不做任何转换
                // 最终返回 Flux<ChatResponse>，每个 ChatResponse 对象包含更完整的模型响应信息 (包括文本内容和元数据等)

        // 整个 streamChatResponse 方法的流程：
        // 1.接收用户通过 HTTP GET 请求传递的 "input" 参数作为用户输入
        // 2.构建一个 Prompt，包含用户输入的消息
        // 3.使用 ChatClient 发起流式聊天请求，将 Prompt 发送给 AI 模型
        // 4.直接将模型返回的流式 ChatResponse 对象作为 Flux<ChatResponse> 返回
        // 5.Spring WebFlux 会将返回的 Flux<ChatResponse> 作为 Server-Sent Events (SSE) 流式响应返回给客户端
        // 6.客户端可以接收到完整的 ChatResponse 对象，包含更详细的响应信息
    }






}
